class TileMovement
{
  TileContainer Container;
  TileMovement(TileContainer container)
  {
    Container=container;
  }
  TileMovement()
  {
  }
  void swapShuffle(PVector from, PVector to)
  {
    //najprosztrza zamiana w tablicy
    Tile t=Container.Container[(int)from.x][(int)from.y];
    Container.Container[(int)from.x][(int)from.y]=Container.Container[(int)to.x][(int)to.y];
    Container.Container[(int)to.x][(int)to.y]=t;
  }
  Boolean swap(PVector from, PVector to, Boolean check)
  {
    float Distance=0.0f;


    switch(DIFFICULT)
    {
    case EASY:       
      Distance=max(abs(from.x - to.x), abs(from.y - to.y)); 
      break;
    case MEDIUM:       
      Distance=abs(from.x - to.x) + abs(from.y - to.y); 
      break;
    }
   
    if (Distance==1)
    {

      //najprosztrza zamiana w tablicy
      Tile t=Container.Container[(int)from.x][(int)from.y];
      Container.Container[(int)from.x][(int)from.y]=Container.Container[(int)to.x][(int)to.y];
      Container.Container[(int)to.x][(int)to.y]=t;
      //sprawdzenie czy wszystkie taile sa ustawione dobrze, czyli wygrana
      if (check)
      {
        checkWinner();
      }
      return true;
    } else
    {
      return false;
    }
  }
  /*
  sprawdzanie czy ułożenie jest dobre. Jeżeli wszystkie będą po kolei to wtedy zmienna 
   WINNER ustawiana jest na true i jest koniec gry. 
   */
  void checkWinner()
  { 
    int counter=0;
    for (int i=0; i<Container.Dimension; i++)
    {
      for (int j=0; j<Container.Dimension; j++)
      {
        if (Container.Container[i][j].TileNumber!=counter)
        {
          //  println(str(Container.Container[i][j].TileNumber));
          return ;
        }
        counter++;
      }
    }

    WINNER=true;
    println(str(WINNER));
  }

  Boolean checkWinnerToShuffle()
  { 
    int counter=0;
    for (int i=0; i<Container.Dimension; i++)
    {
      for (int j=0; j<Container.Dimension; j++)
      {
        if (Container.Container[i][j].TileNumber!=counter)
        {
          return false ;
        }
        counter++;
      }
    }

    return true;
  }
  PVector getEmpty()
  {
    for (int j = 0; j < DIMENSION; ++j) {
      for (int k = 0; k < DIMENSION; ++k) {
        if (Container.Container[j][k].TilePosition.y==0 && Container.Container[j][k].TilePosition.x==0)
        {
          return new PVector(j, k);
        }
      }
    }
    return new PVector(0, 0);
  }
  //Funkcja mieszajaca polegajaca na zwykłym przejsciu kilkukrotnie ruchów, takich jakby użył gracz
  PVector shuffle()
  {
    int i = DIMENSION * DIMENSION - 1;
    while (i > 0) {
      int j = floor(random(0, 1) * i);
      int xi = i % DIMENSION;
      int yi = floor(i / DIMENSION);
      int xj = j % DIMENSION;
      int yj = floor(j / DIMENSION);
      swapShuffle(new PVector(xi, yi), new PVector( xj, yj));
      --i;
    }

    return getEmpty();
  }
  int countInversions(int i, int j) {
    int inversions = 0;
    int tileNum = j * DIMENSION + i;
    int lastTile = DIMENSION * DIMENSION;
    int tileValue = (int)Container.Container[i][j].TilePosition.y* DIMENSION + (int)Container.Container[i][j].TilePosition.x;
    for (int q = tileNum + 1; q < lastTile; ++q) {
      int k = q % DIMENSION;
      int l = floor(q / DIMENSION);

      int compValue = (int)Container.Container[i][j].TilePosition.y * DIMENSION +(int)Container.Container[i][j].TilePosition.x;
      if (tileValue > compValue && tileValue != (lastTile - 1)) {
        ++inversions;
      }
    }
    return inversions;
  }
  int sumInversions() {
    int inversions = 0;
    for (int j = 0; j < DIMENSION; ++j) {
      for (int i = 0; i < DIMENSION; ++i) {
        inversions += countInversions(i, j);
      }
    }
    return inversions;
  }
  Boolean isSolvable(int width, int  height, int  emptyRow) {
    if (width % 2 == 1) {
      return (sumInversions() % 2 == 0);
    } else {
      return ((sumInversions() + height - emptyRow) % 2 == 0);
    }
  }
}

